#include "klippy/core.h"
#include "klippy/config.h"
#include "klippy/reactor.h"
#include "klippy/printer_object.h"
#include <iostream>
#include <stdexcept>
#include <unordered_map>
#include <memory>

constexpr const char* DEFAULT_CONFIG_FILE = "klippy.conf";

namespace klippy {

Printer::Printer() 
    : state_(PrinterState::DISCONNECTED),
      state_message_("Printer initialized"),
      reactor_(create_reactor()) {
    std::cout << "Printer constructor" << std::endl;
}

Printer::~Printer() {
    std::cout << "Printer destructor" << std::endl;
    if (state_ != PrinterState::SHUTDOWN) {
        invoke_shutdown("Printer destructor called");
    }
}

void Printer::initialize() {
    set_state(PrinterState::CONNECTING, "Initializing printer...");
    std::cout << "Initializing Klippy C++ printer" << std::endl;
    
    try {
        auto config = Config::load(DEFAULT_CONFIG_FILE);
        if (!config) {
            std::cerr << "Warning: Failed to load config file: " << DEFAULT_CONFIG_FILE << std::endl;
            std::cerr << "Using default configuration values" << std::endl;
        } else {
            std::cout << "Successfully loaded config file: " << DEFAULT_CONFIG_FILE << std::endl;
        }
        
        reactor_->run();
        set_state(PrinterState::READY, "Printer ready");
    } catch (const std::exception& e) {
        set_state(PrinterState::ERROR, std::string("Initialization error: ") + e.what());
        throw;
    }
}

void Printer::run() {
    try {
        std::cout << "Starting Klippy C++ main loop" << std::endl;
        reactor_->run();
    } catch (const std::exception& e) {
        invoke_shutdown(std::string("Runtime error: ") + e.what());
    }
}

void Printer::add_object(const std::string& name, 
                        std::shared_ptr<PrinterObject> obj) {
    if (objects_.count(name)) {
        throw std::runtime_error("Object already registered: " + name);
    }
    objects_[name] = obj;
}

std::shared_ptr<PrinterObject> Printer::lookup_object(const std::string& name) {
    auto it = objects_.find(name);
    if (it == objects_.end()) {
        throw std::runtime_error("Object not found: " + name);
    }
    return it->second;
}

void Printer::register_event_handler(const std::string& event,
                                   std::function<void()> handler) {
    event_handlers_[event].push_back(handler);
}

/**
 * Send an event to all registered handlers for that event.
 * 
 * @param event The event name to trigger
 * 
 * This will asynchronously invoke all callbacks registered for the given event
 * via the reactor's async callback system. If no handlers are registered for
 * the event, this function does nothing.
 */
void Printer::send_event(const std::string& event) {
    auto it = event_handlers_.find(event);
    if (it != event_handlers_.end()) {
        for (auto& handler : it->second) {
            reactor_->register_async_callback(handler);
        }
    }
}

PrinterState Printer::get_state() const {
    return state_;
}

std::string Printer::get_state_message() const {
    return state_message_;
}

void Printer::set_state(PrinterState state, const std::string& message) {
    state_ = state;
    state_message_ = message;
    send_event("state:" + std::to_string(static_cast<int>(state)));
}

void Printer::invoke_shutdown(const std::string& message) {
    set_state(PrinterState::SHUTDOWN, message);
    for (auto& [name, obj] : objects_) {
        obj->shutdown();
    }
    reactor_->stop();
}

} // namespace klippy